LIBRARY ieee;
USE ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
Use work.transmit_package.all;

ENTITY MIIFSM IS
	PORT
	(	Clock							: IN	STD_LOGIC;
		FrameAReady						: In Std_logic;
		FrameADone						: In Std_logic;
		FrameAnextNibble				: In Std_logic_vector(3 downto 0);
		FrameAReadOut					: Out Std_logic;
		FrameBReady						: In Std_logic;
		FrameBDone						: In Std_logic;
		FrameBnextNibble				: In Std_logic_vector(3 downto 0);
		FrameBReadOut					: Out Std_logic;
		TX_CLK							: Out Std_logic;
		TX_EN							: Out Std_logic;
		TXD								: Out Std_logic_vector(3 downto 0);
		TX_ER							: Out Std_logic;
		CRC								: Buffer Std_logic_vector(31 downto 0)
	);
END MIIFSM;	

ARCHITECTURE Behavior OF MIIFSM IS
	TYPE State_type IS (RESET_state, Preamble_state, SFD_state, Stream_state, CRC_state, Frame_delay);
	SIGNAL state		: State_type;
	Signal readA		: Std_logic;
	Signal streaming	: Std_logic;
	
	Signal buffer1		: Std_logic_vector(3 downto 0);
	Signal buffer2		: Std_logic_vector(3 downto 0);
	Signal buffer3		: Std_logic_vector(3 downto 0);
	Signal buffer4		: Std_logic_vector(3 downto 0);
	Signal buffer5		: Std_logic_vector(3 downto 0);
	Signal buffer6		: Std_logic_vector(3 downto 0);
	Signal buffer7		: Std_logic_vector(3 downto 0);
	Signal buffer8		: Std_logic_vector(3 downto 0);
	
	Signal crcbuffer1		: Std_logic_vector(31 downto 0);
	Signal crcbuffer2		: Std_logic_vector(31 downto 0);
	Signal crcbuffer3		: Std_logic_vector(31 downto 0);
	Signal crcbuffer4		: Std_logic_vector(31 downto 0);
	Signal crcbuffer5		: Std_logic_vector(31 downto 0);
	Signal crcbuffer6		: Std_logic_vector(31 downto 0);
	Signal crcbuffer7		: Std_logic_vector(31 downto 0);
	Signal crcbuffer8		: Std_logic_vector(31 downto 0);
	
	Signal preambleCount		: Integer;
	Signal SFDcount				: Integer;
	--Signal 					: Integer;
	Signal CRCoutCount			: Integer;
	Signal interFrameCounter	: Integer;
	
	Constant Zeros		: Std_logic_vector(3 downto 0) := "0000";
	Constant Pre		: Std_logic_vector(3 downto 0) := "1010";
	Constant SFD		: Std_logic_vector(3 downto 0) := "1011";
BEGIN
	PROCESS
	BEGIN	
	
		TX_CLK <= Clock;
	
		Wait Until Clock'Event And Clock = '1';
		
		Case state Is
			When RESET_state =>
				If FrameAReady = '1' Then
					state <= Preamble_state;
					preambleCount <= 1;
					SFDcount <= 1;
					streaming <= '1';
					readA <= '1';
				Elsif FrameBReady = '1' Then
					state <= Preamble_state;
					preambleCount <= 1;
					SFDcount <= 1;
					streaming <= '1';
					readA <= '0';
				Else
					state <= RESET_state;
				End If;
				TX_EN <= '0';
				If streaming = '1' Then				
					TX_ER <= '1';
				Else
					TX_ER <= '0';
				End If;
				TXD <= Zeros;
				FrameAReadOut <= '0';
				FrameBReadOut <= '0';
			When Preamble_state =>
				If preambleCount = 14 Then
					state <= SFD_state;
				Else
					state <= Preamble_state;
					preambleCount <= preambleCount + 1;
				End If;
				
				If preambleCount = 7 Then
					If readA = '1' Then
						FrameAReadOut <= '1';
					Else
						FrameBReadOut <= '1';
					End If;
				Else
					FrameAReadOut <= '0';
					FrameBReadOut <= '0';
				End If;
				buffer1 <= buffer2;
				buffer2 <= buffer3;
				buffer3 <= buffer4;
				buffer4 <= buffer5;
				buffer5 <= buffer6;
				buffer6 <= buffer7;
				buffer7 <= buffer8;
				If readA = '1' Then
					buffer8 <= FrameAnextNibble;
				Else
					buffer8 <= FrameBnextNibble;
				End If;
				
				TX_EN <= '1';
				TX_ER <= '0';
				TXD <= Pre;
			When SFD_state =>
				If SFDcount = 2 Then
					state <= Stream_state;
					TXD <= SFD;
					-- Initialize CRC register with first 32 bits of DA
					CRC(3 downto 0) <= buffer1;
					CRC(7 downto 4) <= buffer2;
					CRC(11 downto 8) <= buffer3;
					CRC(15 downto 12) <= buffer4;
					CRC(19 downto 16) <= buffer5;
					CRC(23 downto 20) <= buffer6;
					CRC(27 downto 24) <= buffer7;
					CRC(31 downto 28) <= buffer8;
					--DACount <= 1;
				Else
					state <= SFD_state;
					SFDcount <= SFDcount + 1;
					TXD <= Pre;
					FrameAReadOut <= '0';
					FrameBReadOut <= '0';
				End If;
				
				buffer1 <= buffer2;
				buffer2 <= buffer3;
				buffer3 <= buffer4;
				buffer4 <= buffer5;
				buffer5 <= buffer6;
				buffer6 <= buffer7;
				buffer7 <= buffer8;
				If readA = '1' Then
					buffer8 <= FrameAnextNibble;
				Else
					buffer8 <= FrameBnextNibble;
				End If;
				
				TX_EN <= '1';
				TX_ER <= '0';
			When Stream_state =>
				If readA = '1' Then
					If FrameADone = '1' Then
						state <= CRC_state;
						CRCoutCount <= 1;
					Else
						state <= Stream_state;
					End If;
					
					TXD <= buffer1; --FrameAnextNibble
					buffer8 <= FrameAnextNibble;
				Else
					If FrameBDone = '1' Then
						state <= CRC_state;
						CRCoutCount <= 1;
					Else
						state <= Stream_state;
					End If;
					TXD <= buffer1; --FrameBnextNibble;
					buffer8 <= FrameBnextNibble;
				End If;
				buffer1 <= buffer2;
				buffer2 <= buffer3;
				buffer3 <= buffer4;
				buffer4 <= buffer5;
				buffer5 <= buffer6;
				buffer6 <= buffer7;
				buffer7 <= buffer8;
				
				CRC <= nextCRC32(buffer1, CRC);
				crcbuffer8 <= CRC;
				crcbuffer7 <= crcbuffer8;
				crcbuffer6 <= crcbuffer7;
				crcbuffer5 <= crcbuffer6;
				crcbuffer4 <= crcbuffer5;
				crcbuffer3 <= crcbuffer4;
				crcbuffer2 <= crcbuffer3;
				crcbuffer1 <= crcbuffer2;
				
				TX_EN <= '1';
				TX_ER <= '0';
				FrameAReadOut <= '0';
				FrameBReadOut <= '0';
			When CRC_state =>
				If CRCoutCount = 8 Then
					state <= Frame_delay;
					interFrameCounter <= 1;
					streaming <= '0';
				Else
					state <= CRC_state;
					CRCoutCount <= CRCoutCount + 1;
				End If;
				
				Case CRCoutCount Is
					When 1 =>
						TXD <= crcbuffer1(3 downto 0);
					When 2 =>
						TXD <= crcbuffer1(7 downto 4);
					When 3 =>
						TXD <= crcbuffer1(11 downto 8);
					When 4 =>
						TXD <= crcbuffer1(15 downto 12);
					When 5 =>
						TXD <= crcbuffer1(19 downto 16);
					When 6 =>
						TXD <= crcbuffer1(23 downto 20);
					When 7 =>
						TXD <= crcbuffer1(27 downto 24);
					When 8 =>
						TXD <= crcbuffer1(31 downto 28);
					When Others =>
						TXD <= Zeros;
				End Case;

				TX_EN <= '1';
				TX_ER <= '0';
				FrameAReadOut <= '0';
				FrameBReadOut <= '0';
			When Frame_delay =>
				If interFrameCounter = 24 Then
					state <= RESET_state;
				Else
					state <= Frame_delay;
					interFrameCounter <= interFrameCounter + 1;
				End If;
				TX_EN <= '0';
				TX_ER <= '0';
				TXD <= Zeros;
				FrameAReadOut <= '0';
				FrameBReadOut <= '0';
		End Case;
		
	END PROCESS;

END Behavior;
